#pragma once

#include <queue>
#include <ctime>
#include <functional>
#include <memory>

/// Timer type
enum class Type_ {
    NONE = 0,
    ONCE = 1,
    LOOP,
};
/// Timer callback
using TimerFunc = std::function<void(std::uint64_t)>;
/// Timer node
struct TimerNode_ {
    std::time_t trigger_time {std::time(0)};
    Type_ type {Type_::NONE};
    std::time_t duration {std::time(0)};
    bool operator<(const TimerNode_& node) {
        return trigger_time > node.trigger_time;
    }
    std::uint64_t user_data {std::uint64_t(0)};
    TimerFunc timer_func;
    bool deleted {false};
};

using TimerHandle = std::shared_ptr<TimerNode_>;

/// Timer manager
class Timer {
    using TimerQueue = std::priority_queue<TimerHandle>;
    TimerQueue queue_; ///< Timer queue

public:
    ~Timer();
    /// Start a timer
    /// @param timer_func timer callback
    /// @param duration timeout duration
    /// @param user_data user data
    /// @return timer handle
    TimerHandle addTimerOnce(TimerFunc timer_func, std::time_t duration, std::uint64_t user_data);
    /// Start a loop timer, reserved
    /// @param timer_func timer callback
    /// @param duration timeout duration
    /// @param user_data user data
    /// @return timer handle
    TimerHandle addTimer(TimerFunc timer_func, std::time_t duration, std::uint64_t user_data);
    /// Cancel a timer, reserved
    void cancelTimer(TimerHandle handle);
    /// Returns a expired timer
    /// @param now current timestamp in millionsecond
    TimerHandle runOnce(std::time_t now = 0);
    /// Clear all timer
    void clear();

private:
    TimerHandle runOnce_(std::time_t now);
};

extern std::time_t getMilliSeconds();
